﻿using System;
using Tree;

namespace calculator
{
    class Parser : IParser, IDisposable
    {
        public Parser(Scanner scanner, SymbolTable symtab,
            Store store, FunctionTable functab)
        {
            _scanner = scanner;
            _symtab = symtab;
            _status = Status.Ok;
            _functab = functab;
            _store = store;
        }
        public Status Eval()
        {
            Parse();
            if (_status == Status.Ok)
                Execute();
            else
                _status = Status.Quit;
            return _status;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    //disposed managed resource
                    _tree = null;
                }
                // release unmanaged resource
            }
            _disposed = true;

            // if available, call to base class
            // base.Dispose(disposing);
        }


        private bool _disposed = false;

        private void Parse()
        {
            _tree = Expr();
        }
        private Node Expr()
        {
            //Expression starts with a term.  what follows then is 
            //operators +, -, =, or nothing
            //Expression is Term + Expression
            //or Term - Expression
            //or Term = Expression
            //or just Term

            Node pNode = Term();
            EToken token = _scanner.Token();
            if (token == EToken.Plus)
            {
                _scanner.Accept();
                Node pRight = Expr();
                pNode = new AddNode(pNode, pRight);
            }
            else if (token == EToken.Minus)
            {
                _scanner.Accept();
                Node pRight = Expr();
                pNode = new SubNode(pNode, pRight);
            }
            else if (token == EToken.Assign)
            {
                _scanner.Accept();
                Node pRight = Expr();
                if (pNode.IsLvalue())
                    pNode = new AssignNode(pNode, pRight);
                else
                {
                    _status = Status.Error;
                    pNode = null;
                    pNode = Expr();
                }
            }
            return pNode;
        }
        private Node Term()
        {
            // term is Factor * Term
            //or Factor / Term
            //or just Factor

            Node pNode = Factor();
            if (_scanner.Token() == EToken.Mult)
            {
                _scanner.Accept();
                Node pRight = Term();
                pNode = new MultNode(pNode, pRight);
            }
            else if (_scanner.Token() == EToken.Divide)
            {
                _scanner.Accept();
                Node pRight = Term();
                pNode = new DivideNode(pNode, pRight);
            }
            return pNode;
        }
        private Node Factor()
        {
            //Factor is ( Expression ) - parenthesized expression
            //or Number - literal number(floating point)
            //or Identifer - user variable
            //or -Factor - unary minus

            Node pNode;
            EToken token = _scanner.Token();

            if (token == EToken.LParen)
            {
                _scanner.Accept(); //accepts '('
                pNode = Expr(); // eats the expression
                if (_scanner.Token() != EToken.RParen)
                    _status = Status.Error;
                _scanner.Accept(); //accepts ')'
            }
            else if (token == EToken.Number)
            {
                pNode = new NumNode(_scanner.Number());
                _scanner.Accept();
            }
            else if (token == EToken.Ident)
            {
                string symbol;
                int lensymbol;

                //copy symbol to symbol
                _scanner.SymbolName(out symbol, out lensymbol);
                int id = _symtab.Find(symbol);
                _scanner.Accept();

                if (_scanner.Token() == EToken.LParen) //function call
                {
                    _scanner.Accept(); // accept '('
                    pNode = Expr(); // eat expression
                    if (_scanner.Token() == EToken.RParen)
                        _scanner.Accept(); // accept ')'
                    else
                        _status = Status.Error;
                    if (id != -1 /*idNotFound from stringtable*/ && id < _functab.Size())
                    {
                        pNode = new FuncNode(_functab.GetFunction(id), pNode);
                    }
                    else
                    {
                        Console.WriteLine("Unknown Function");
                        Console.WriteLine(symbol);
                    }
                }
                else
                {
                    if (id == -1) //idNotFound from stringtable
                        id = _symtab.ForceAdd(symbol);
                    pNode = new VarNode(id, _store);
                }
            }
            else if (token == EToken.Minus) //unary minus
            {
                _scanner.Accept();
                pNode = new UMinusNode(Factor());
            }
            else
            {
                _scanner.Accept();
                _status = Status.Error;
                pNode = null;
            }
            return pNode;
        }
        private void Execute()
        {
            if (_tree != null)
            {
                double result = _tree.Result;
                Console.WriteLine(" {0}", result);
            }
        }


        private Scanner _scanner;
        private SymbolTable _symtab;
        private Node _tree;
        private Status _status;
        private Store _store;
        private FunctionTable _functab;
    }
}
